package values

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"sync"
	"time"
	"unsafe"

	"github.com/spf13/cast"
)

// ToBase 转换 any 目标为 TypeBase 泛型T, 如果转换失败, 则返回T零值
//   - TypeBase: string | bool | int | int8 | int16 | int32 | int64 |	uint | uint8 | uint16 | uint32 | uint64 |	float32 | float64 |	time.Time | time.Duration | time.Month | time.Weekday
//   - 注意: 处理time.Time | time.Duration 类型时, 如待转换的any目标是日期时间字符串为包含时区信息时则使用UTC时区
func ToBase[T TypeBase](v any) T {
	to, _ := ToBaseE[T](v)
	return to
}

// ToBase 转换 any 目标为 TypeBase 泛型T, 如果转换失败, 则返回defaultVal指定的默认值
//   - TypeBase: string | bool | int | int8 | int16 | int32 | int64 |	uint | uint8 | uint16 | uint32 | uint64 |	float32 | float64 |	time.Time | time.Duration | time.Month | time.Weekday
//   - 注意: 处理time.Time | time.Duration 类型时, 如待转换的any目标是日期时间字符串为包含时区信息时则使用UTC时区
func ToBaseD[T TypeBase](v any, defaultVal T) T {
	if to, err := ToBaseE[T](v); err == nil {
		return to
	}
	return defaultVal
}

// ToBaseE 转换 any 目标为 TypeBase 泛型T, 如果转换失败, 则返回T零值和错误信息
//   - TypeBase: string | bool |int | int8 | int16 | int32 | int64 |	uint | uint8 | uint16 | uint32 | uint64 |	float32 | float64 |	time.Time | time.Duration | time.Month | time.Weekday
//   - 注意: 处理time.Time | time.Duration 类型时, 如待转换的any目标是日期时间字符串为包含时区信息时则使用UTC时区
func ToBaseE[T TypeBase](v any) (toVal T, err error) {
	return _anyToBase[T](v)
}
func _anyToBase[T any](v any) (toVal T, err error) {
	if v == nil {
		return toVal, errors.New("ToBaseE: nil pointer not support")
	}
	v, err = _anyToBaseParseV[T](v)
	if err != nil {
		return toVal, err
	}
	var vs any
	switch any(toVal).(type) {
	default:
		err = fmt.Errorf("unable to (%#v) type %T of type %T", v, v, toVal)
	case string:
		vs, err = ToStringE(v)
	case bool:
		vs, err = ToBoolE(v)
	case int:
		vs, err = cast.ToIntE(v)
	case int8:
		vs, err = cast.ToInt8E(v)
	case int16:
		vs, err = cast.ToInt16E(v)
	case int32:
		vs, err = cast.ToInt32E(v)
	case int64:
		vs, err = cast.ToInt64E(v)
	case uint:
		vs, err = cast.ToUintE(v)
	case uint8:
		vs, err = cast.ToUint8E(v)
	case uint16:
		vs, err = cast.ToUint16E(v)
	case uint32:
		vs, err = cast.ToUint32E(v)
	case uint64:
		vs, err = cast.ToUint64E(v)
	case float32:
		vs, err = cast.ToFloat32E(v)
	case float64:
		vs, err = cast.ToFloat64E(v)
	case time.Time:
		vs, err = cast.ToTimeE(v)
	case time.Duration:
		vs, err = cast.ToDurationE(v)
	case time.Month:
		vs, err = ToTimeMonthE(v)
	case time.Weekday:
		vs, err = ToTimeWeekdayE(v)
	case error:
		if vError, ok := v.(error); ok {
			vs = vError
		} else {
			err = fmt.Errorf("unable to(%#v) of type %T to error", v, v)
		}
	case any:
		vs = v
	}
	if err == nil {
		if to, ok := vs.(T); ok {
			return to, nil
		}
		return toVal, fmt.Errorf("ToBaseE: unable to (%#v) type %T of type %T", v, v, toVal)
	}
	return toVal, fmt.Errorf("ToBaseE: %s", err.Error())
}

// _anyToBaseParseV 处理 any 目标为 TypeBase 泛型T, 支持指针类型
func _anyToBaseParseV[T any](v any) (any, error) {
	if v == nil {
		return v, errors.New("ToBaseE: nil pointer not support")
	}
	isString := false
	switch vs := v.(type) {
	case bool,
		int, int8, int16, int32, int64,
		uint, uint8, uint16, uint32, uint64,
		float32, float64,
		complex64, complex128,
		time.Time, time.Duration, time.Location, time.Month, time.Weekday, time.Ticker, time.Timer,
		json.Number, error, uintptr:
		return v, nil
	case string:
		isString = true
	case *string:
		isString = true
		v = *vs
	case *bool:
		v = *vs
	case *int:
		v = *vs
	case *int8:
		v = *vs
	case *int16:
		v = *vs
	case *int32:
		v = *vs
	case *int64:
		v = *vs
	case *uint:
		v = *vs
	case *uint8:
		v = *vs
	case *uint16:
		v = *vs
	case *uint32:
		v = *vs
	case *uint64:
		v = *vs
	case *float32:
		v = *vs
	case *float64:
		v = *vs
	case *time.Time:
		v = *vs
	case *time.Duration:
		v = *vs
	case *time.Weekday:
		v = *vs
	case *time.Month:
		v = *vs
	case *json.Number:
		v = *vs
	case *error:
		v = *vs
	case *uintptr:
		v = *vs
	}
	// 预处理 nil
	if v == nil {
		return v, errors.New("ToBaseE: nil pointer not support")
	}
	// 预处理 string To Number
	if isString {
		vs := v.(string)
		var toVal T
		switch any(toVal).(type) {
		case string, bool:
			return v, nil
		case int, int8, int16, int32, int64:
			if vs == "" {
				return vs, errors.New("ToBaseE: empty string \"\" to intX  not support")
			}
			if IsFloat(vs) {
				return strings.Split(vs, ".")[0], nil
			}
			return vs, nil
		case uint, uint8, uint16, uint32, uint64:
			if vs == "" {
				return vs, errors.New("ToBaseE: empty string \"\" to uintX not support")
			}
			if IsFloat(vs) {
				if vs[0] == '-' {
					return vs, errors.New("ToBaseE: negative string \"-xxx\" to uintX not support")
				}
				return strings.Split(vs, ".")[0], nil
			}
			return vs, nil
		case float32, float64:
			if vs == "" {
				return vs, errors.New("ToBaseE: empty string \"\" to float32 or float64 not support")
			}
			if IsFloat(vs) {
				// trim zero decimal
				vs = strings.TrimRight(vs, "0")
				vs = strings.TrimRight(vs, ".")
			}
			return vs, nil
		case time.Time, time.Duration:
			if vs == "" {
				return vs, errors.New("ToBaseE: empty string \"\" to time.Time or time.Duration not support")
			}
			if IsFloat(vs) {
				// trim zero decimal
				vs = strings.TrimRight(vs, "0")
				vs = strings.TrimRight(vs, ".")
			}
			return vs, nil
		}
	}
	return v, nil
}

// IsTypeBase 判断 any 目标 是否为 TypeBase 泛型T
func IsTypeBase(v any) bool {
	if v == nil {
		return false
	}
	switch v.(type) {
	case string, bool,
		int, int8, int16, int32, int64,
		uint, uint8, uint16, uint32, uint64,
		float32, float64,
		time.Time, time.Duration, time.Month, time.Weekday:
		return true
	default:
		return false
	}
}

// IsTypeBasePtr 判断 any 目标 是否为指向 TypeBase 泛型T的指针
func IsTypeBasePtr(v any) bool {
	if v == nil {
		return false
	}
	switch v.(type) {
	case *string, *bool,
		*int, *int8, *int16, *int32, *int64,
		*uint, *uint8, *uint16, *uint32, *uint64,
		*float32, *float64,
		*time.Time, *time.Duration, *time.Month, *time.Weekday:
		return true
	default:
		return false
	}
}

// _isTypeBasic 判断 any 目标 是否为Golang 常用的基本类型或指针
func _isTypeBasic(v any) bool {
	switch v.(type) {
	case nil,
		string, bool,
		int, int8, int16, int32, int64,
		uint, uint8, uint16, uint32, uint64, uintptr,
		float32, float64,
		complex64, complex128,
		time.Time, time.Duration, time.Location, time.Month, time.Weekday, time.Ticker, time.Timer,
		struct{}, chan any, func(), unsafe.Pointer,
		bytes.Buffer, strings.Builder,
		reflect.Type, reflect.Value, reflect.Kind,
		sync.Mutex, sync.RWMutex, sync.Cond, sync.Once, sync.Pool, sync.WaitGroup, sync.Locker,
		error,
		json.Number,
		*string, *bool,
		*int, *int8, *int16, *int32, *int64,
		*uint, *uint8, *uint16, *uint32, *uint64, *uintptr,
		*float32, *float64,
		*complex64, *complex128,
		*time.Time, *time.Duration, *time.Location, *time.Month, *time.Weekday, *time.Ticker, *time.Timer,
		*struct{}, *chan any, *func(), *unsafe.Pointer,
		*bytes.Buffer, *strings.Builder,
		*reflect.Type, *reflect.Value, *reflect.Kind,
		*error,
		*json.Number:
		return true
	default:
		return false
	}
}
